Understand Coroutine in Unity

Here is a basic understand on how coroutine works in Unity.

IEnumerator

Before going into coroutine, we should know what is IEnumerator.

Supports a simple iteration over a non-generic collection.

IEnumerator should be used with yield statement. When the return type of a function is IEnumerator, it works in a iteration way and assigned to variable with type IEnumerator. Every time MoveNext() of the variable is called, the function will move to the next yield statement and return.

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private IEnumerator ien;

// Use this for initialization
void Start () {
ien = ExampleIEN ();

while (ien.MoveNext ()) {
Debug.Log (ien.Current);
}
}

IEnumerator ExampleIEN(){
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}

Output:

1
2
3
4
1
2
3
4

Coroutine

Then we start talk about Coroutine.
Coroutine is a powerful mechanic to control the pause and resume of a function. Usually for example when you have to call a function every three frames or every three frames, it’s really stupid and computational expensive to do the stuff in the Update() function. That’s why we use instead the Coroutine.

The execution of a coroutine can be paused at any point using the yield statement. The yield return value specifies when the coroutine is resumed. Coroutines are excellent when modelling behaviour over several frames. Coroutines have virtually no performance overhead. StartCoroutine function always returns immediately, however you can yield the result. This will wait until the coroutine has finished execution. There is no guarantee that coroutines end in the same order that they were started, even if they finish in the same frame.

Coroutine is like an integration of while(ien.MoveNext()). It pauses when reaches the yield statement (yield instruction), and resumes after the execution of the yield instruction.

We use StartCoroutine() to start a new coroutine. The main function will continues after the first yield return from the coroutine.

Here we show a example code.
Code:

1
2
3
4
5
6
7
8
9
10
void Start () {
StartCoroutine (ExampleFunc ());
Debug.Log ("2");
}

IEnumerator ExampleFunc(){
Debug.Log ("1");
yield return new WaitForSeconds(1);
Debug.Log ("3");
}

Output:

1
2
3
1
2
3

The logic can be described as following:

  1. Start a coroutine ExampleFunc()
  2. ExampleFunc shows 1
  3. ExampleFunc pauses and returns
  4. Main function (Start) shows 2
  5. After 1 second, ExampleFunc resumes and show 3
  6. ExampleFunc reaches end and finish

Here we show a more complex example (Nested Coroutine)
Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Start () {
Debug.Log ("1");
StartCoroutine (ExampleFunc1 ());
Debug.Log ("4");

}

IEnumerator ExampleFunc1(){
Debug.Log ("2");
yield return StartCoroutine (ExampleFunc2 ());
Debug.Log ("6");

}

IEnumerator ExampleFunc2(){
Debug.Log ("3");
yield return new WaitForSeconds (2);
Debug.Log ("5");
}

Output:

1
2
3
4
5
6
1
2
3
4
5
6

The logic can be described as following:

  1. Main function (Start) shows 1
  2. Main function starts a coroutine ExampleFunc1()
  3. ExampleFunc1 shows 2
  4. ExampleFunc1 starts a coroutine ExampleFunc2()
  5. ExampleFunc2 shows 3
  6. ExampleFunc2 pauses and returns
  7. ExampleFunc1 pauses and returns
  8. Main function shows 4
  9. ExampleFunc2 resumes and showes 5 (after the execution of WaitForSeconds()
  10. ExampleFunc1 resumes and showes 6 (after the execution of ExampleFunc2()

Yield Instruction

Usually we have 5 types of yield instructions in coroutine.

  • null: nothing
  • WaitForSecond(): wait for specific seconds
  • WaitForFixUpdate(): wait until next fixed frame rate update function
  • WWW: wait until a download finishes
  • StartCoroutine(): nested coroutine

Coroutine and Thread

Coroutine doesn’t create a new thread, but instead use a concurrent mechanic.

All coroutine can do can also be done by multi-threads. However, as we know, Unity is not thread-safe, which means you have to spend a lot of time on the synchronization of data.

Therefore, for some simple tasks to control, coroutine should be best way. For some computational expensive tasks, multi-threads is better. (When task is out of computation, coroutine will stop but thread won’t.)